#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int servidor(int puerto){

	//buffer para leer
	char buf[1024]; 
	int sockd, sockd_conn;
	int nread;
	//Guarda el tamaño de la direccion.
	socklen_t addrlen; 

	//Estructuras de las direcciones cliente y servidor
	struct sockaddr_in addr_srv;
	struct sockaddr_in addr_cli; 

	//Creo el socket, PF_INET = IPv4
	if ( (sockd = socket(PF_INET, SOCK_STREAM,0)) < 0) { 
		perror("socket()");
		return -1;
	}
	
	// address family 
	addr_srv.sin_family = AF_INET;
	//designo un puerto a usar
	addr_srv.sin_port = htons(puerto);
	//Address
	addr_srv.sin_addr.s_addr = htonl(INADDR_ANY);

	int opt = 1;
	//para que se pueda re usar el puerto sin problemas
	if(setsockopt(sockd,SOL_SOCKET,SO_REUSEADDR,(char*)&opt,sizeof(opt))<0) 
	{
		perror("setsockopt()");
		return -6;
	}
	
	//hago casteo de la estructura xq espera el tamaño general que tendrá
	//Enlazo con el socket (sockd) con la direccion del servidor, e indico el puerto donde se esperara la conexion
	if(bind(sockd,(struct sockaddr *)&addr_srv, sizeof addr_srv) < 0) 
	{
		perror("bind");
		return -2;
	}

	//"5" cantidad de conexiones pendientes maximas a aceptar
	//Escucho
	if (listen(sockd, 5) < 0) 
	{
		perror("listen()");
		return -3;
	}
	
	//tamaño de la dirección del cliente
	addrlen = sizeof addr_cli;

	while((sockd_conn = accept(sockd,(struct sockaddr *)&addr_cli,&addrlen)) > 0 )
	{
		printf("\ncliente conectado");
		//creo otro proceso que atenderá las nuevas conexiones
		/*En realidad ahora se crea un proceso por cada cliente que se conecta, pero debería crearse un hilo para cada cliente
		o lo que sea más conveniente en términos de memoria, uso CPU*/

		switch(fork()){
		//Trabajo del hijo
		case 0:
			//El hijo cierra el descriptor de archivo del socket
			close(sockd); 
			
			//BUCLE DE COMUNICACION DURANTE LA CONEXION
			do{
				//limpio "buf" para que no me quede basura antes de cada lectura
				memset(buf,0,sizeof buf);
				
				//LECTURA
				//leo lo que venga del buffer con un tamaño "buf", 1024
				if((nread = read(sockd_conn,buf,sizeof buf)) < 0) 
				{
					perror("read()"); 
					return -5;
				}

				//Analizar la entrada para ver si es comando o mensaje
				
				//Si es comando ejecutarlo
					//Si es comando para el servidor enviar al otro cliente solo el mensaje
					//Si es comando de juego enviar al otro cliente el comando mas el mensaje.
		
				/* Info del cliente
					PID del hijo: getpid()
					IP del cliente: inet_ntoa(addr_cli.sin_addr)
					Puerto: ntohs(addr_cli.sin_port)
					Buffer: buf
				*/
		
				//ESCRITURA
				//envio la respuesta
				write(sockd_conn,buf,nread);

			}while(strcmp(buf,"salir\n")!=0);

			//cierro el socket
			close(sockd_conn);

			printf("\ncliente desconectado");	

			//Retorno del hijo
			return 0;

		case -1: //error al crear el hijo
			perror("fork()");
			return -4;
		}
		//el padre cierra el descriptor del hijo para que no vayan quedando demasiadas conexiones abiertas
		close(sockd_conn); 
	}
	
	close(sockd);
	return 0;
}		
